/************************************************************************************
**  
* @copyright (c) 2015-, Technology Co., LTD. All Right Reserved.
*
*************************************************************************************/
/**
* @file     duye_cap.cpp
* @version     
* @brief      
* @author   
* @date     2013-03-23
* @note 
*
*  1. 2018-03-23 created this file
* 
*/
#include <duye_logger.h>
#include <duye_cap.h>

static const int8* DUYE_LOG_PREFIX = "duye.cap";

namespace duye {

PcapCallback Pcap::dispatchPacketPcapCallback = NULL;
PcapCallback Pcap::loopPacketPcapCallback = NULL;

Pcap::Pcap() : m_handle(NULL), m_dumpHandle(NULL) {
	memset(m_errbuf, 0, PCAP_ERRBUF_SIZE);
}

Pcap::~Pcap() {}

bool Pcap::openDevice(const std::string &deviceName, int threadNum) {
	threadNum = threadNum;
	if (m_handle != NULL) {
		closeDevice();
	}

	m_handle = pcap_open_live(const_cast<char *>(deviceName.c_str()),
		1518, 1/*PROMISCUOUS*/, 0, m_errbuf);

	if (m_handle == NULL) {
		DUYE_ERROR("call pcap_open_live() return null");
		return false;
	}

	m_devName = deviceName;

	DUYE_INFO("pcap version %s", pcap_lib_version());

	return true;
}

bool Pcap::closeDevice() {
	if (m_handle != NULL) {
		pcap_close(m_handle);
		m_handle = NULL;
	}

	return true;
}

bool Pcap::openReadFile(const std::string &fileName) {
	if (m_handle != NULL) {
		closeReadFile();
	}

	m_handle = pcap_open_offline(const_cast<char *>(fileName.c_str()), m_errbuf);
	if (m_handle == NULL) {
		DUYE_ERROR("call pcap_open_offline() return null");
		return false;
	}

	DUYE_INFO("pcap version %s", pcap_lib_version());
	return true;
}

bool Pcap::closeReadFile() {
	if (m_handle != NULL) {
		pcap_close(m_handle);
		m_handle = NULL;
	}

	return true;
}

bool Pcap::openDumpFile(const std::string &fileName) {
	if (m_handle == NULL) {
		DUYE_ERROR("m_handle == NULL");
		return false;
	}

	if (m_dumpHandle != NULL) {
		closeDumpFile();
	}	

	m_dumpHandle = pcap_dump_open(m_handle, const_cast<char *>(fileName.c_str()));
	if (m_dumpHandle == NULL) {
		DUYE_ERROR("call pcap_dump_open() return null");
		return false;
	}

	return true;
}

bool Pcap::closeDumpFile() {
	if (m_dumpHandle != NULL) {
		pcap_dump_close(m_dumpHandle);
		m_dumpHandle = NULL;
	}

	return true;
}

bool Pcap::dumpPacketIntoFile(const PcapPkthdr* hdr, const u_char* data) {
	struct pcap_pkthdr* wpcapHdr = (struct pcap_pkthdr*)hdr;
	pcap_dump(static_cast<u_char*>(static_cast<void*>(wpcapHdr)), wpcapHdr, data);
	return true;
}

bool Pcap::dumpPacketFlush() {
	if (m_dumpHandle == NULL) {
		DUYE_ERROR("m_dumpHandle == NULL");
		return false;
	}

	if (pcap_dump_flush(m_dumpHandle) < 0) {
		DUYE_ERROR("pcap_dump_flush failed");
		return false;		
	}

	return true;
}

bool Pcap::setFilter(const std::string &filterRule, int32 optimizationFlag) {
	if (m_handle == NULL) {
		DUYE_ERROR("m_handle == NULL");
		return false;		
	}

	uint32 netmask = 0;
	pcap_if_t* alldevs = NULL;	
	if (pcap_findalldevs(&alldevs, m_errbuf) != 0) {
		DUYE_ERROR("pcap_findalldevs failed : %s", m_errbuf);
		return false;	
	}

	pcap_if* d = NULL;
	for (d = alldevs; d != NULL; d = d->next) {
		if (0 == m_devName.compare(d->name)) {
			if (d->addresses != NULL  && d->addresses->netmask != NULL) {
				netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.s_addr;
			}
			else {
				netmask = 0xffffff; 
			}

			break;
		}
	}
	pcap_freealldevs(alldevs);

	if (pcap_compile(m_handle, &m_bpf, const_cast<char *>(filterRule.c_str()), 
		optimizationFlag, netmask) < 0) {
		DUYE_ERROR("pcap_compile failed : %s", pcap_geterr(m_handle));
		return false;
	}

	if (pcap_setfilter(m_handle, &m_bpf) < 0) {
		DUYE_ERROR("pcap_setfilter failed : %s", pcap_geterr(m_handle));
		return false;		
	}

	return true;
}

int32 Pcap::getDataLinkType() {
	return (pcap_datalink(m_handle));
}

bool Pcap::statistic(uint32* received, uint32* droped, uint32* notSupported) {
	if (m_handle == NULL) {
		DUYE_ERROR("m_handle == NULL");
		return false;		
	}

	struct pcap_stat stat;
	if (pcap_stats(m_handle, &stat) != 0) {
		DUYE_ERROR("pcap_stats failed : %s", pcap_geterr(m_handle));
		return false;			
	}

	*received = stat.ps_recv;
	*droped = stat.ps_drop;
	*notSupported = stat.ps_ifdrop;
	return true;
}

CapturePacket* Pcap::nextPacket() {
	if (m_handle == NULL) {
		DUYE_ERROR("m_handle == NULL");
		return NULL;		
	}

	struct pcap_pkthdr* wpcapHdr = (struct pcap_pkthdr*)(m_packet.first);
	if (1 != pcap_next_ex(m_handle, &wpcapHdr, (const u_char**)&m_packet.second)) {
		return NULL;
	}

	return &m_packet;
}

int32 Pcap::dispatchPacket(int32 capturePacketCount, PcapCallback callBack, void* user) { 
	dispatchPacketPcapCallback = callBack;
	return pcap_dispatch(m_handle, capturePacketCount, (pcap_handler)(Pcap::dispatchPacketCallback), (u_char*)user); 
}

int32 Pcap::loopPacket(int32 capturePacketCount, PcapCallback callBack, void* user) { 
	loopPacketPcapCallback = callBack;
	return pcap_loop(m_handle, capturePacketCount, (pcap_handler)Pcap::loopPacketCallback, (u_char*)user); 
}

bool Pcap::dispatchPacketCallback(u_char* user, const struct pcap_pkthdr* pcapPktHeader, const u_char* pkt) {
	if (dispatchPacketPcapCallback) {
		dispatchPacketPcapCallback((void*)user, (const PcapPkthdr*)pcapPktHeader, (const uint8*)pkt);
	}

	return true;
}

bool Pcap::loopPacketCallback(u_char* user, const struct pcap_pkthdr* pcapPktHeader, const u_char* pkt) { 
	if (loopPacketPcapCallback) {
		loopPacketPcapCallback((void*)user, (const PcapPkthdr*)pcapPktHeader, (const uint8*)pkt); 
	}

	return true;
}

}
